package com.gk.panda.apis.service.impl;

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.gk.panda.apis.service.FdfsService;
import com.gk.panda.apis.util.X509TrustUtiil;
import com.gk.panda.commons.constant.Constants;
import com.gk.panda.commons.exception.ServerException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.annotation.PostConstruct;
import javax.net.ssl.*;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName: FdfsServiceImpl
 * @Description: fdfs文件
 * @author: Sir.yang
 * @date: 2022/11/6 10:10
 * @version: 1.0
 **/
@Service
@Slf4j
public class FdfsServiceImpl implements FdfsService {

    @Autowired
    private FastFileStorageClient fileStorageClient;

    @Value("${fdfsurl}")
    private String fdfsurl;

    @Value("${fdfgroup}")
    private String fdfgroup;

    /**
     * @description: 文件上传
     * @param: []
     * @return: java.lang.String
     * @date: 2022/11/6 10:10
     * @version: 1.0
     **/
    @Override
    public String uploadFile(MultipartFile file, String fileExtName) {
        StorePath storePath = null;
        try {
            storePath = fileStorageClient.uploadFile(file.getInputStream(), file.getSize(), fileExtName, null);
        } catch (Exception e) {
            log.error("文件上传失败：{}", e.getMessage());
            throw new ServerException("文件上传失败");
        }
        if(ObjectUtils.isEmpty(storePath)){
            throw new ServerException("文件接收失败，未获取到文件接收的地址信息！");
        }
        StringBuffer path = new StringBuffer();
        path.append(fdfsurl);
        path.append(storePath.getFullPath());
        return path.toString();
    }


    /** 文件删除
     * @param filePath – 文件路径(groupName/path)
     */
    @Override
    public void deleteFile(String filePath){
        if(StringUtils.isBlank(filePath)){
            throw new ServerException("文件地址不能为空！");
        }
        filePath = filePath.replace(fdfsurl, "").replace(fdfgroup, "").substring(Constants.ONE);
        try{
            fileStorageClient.deleteFile(fdfgroup, filePath);
        }catch (Exception e){
            throw new ServerException(e.getMessage());
        }
    }

    @Override
    public String updateFromIcc(String path) {
        //判断http和https
        File file = null;
        if (path.startsWith("https://")) {
            file = getNetUrlHttps(path);
        } else {
            file = getNetUrlHttp(path);
        }

        FileItem item = new DiskFileItemFactory().createItem("file"
                , MediaType.MULTIPART_FORM_DATA_VALUE
                , true
                , file.getName());
        try (InputStream input = new FileInputStream(file);
             OutputStream os = item.getOutputStream()) {
            // 流转移
            IOUtils.copy(input, os);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid file: " + e, e);
        }
        MultipartFile multipartFile = new CommonsMultipartFile(item);

        //获取文件上传的名称
        String originalFilename = multipartFile.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new ServerException("上传文件有误，请确认！");
        }
        String[] fileNameAttr = originalFilename.split("\\.");
        //获取文件的后缀
        String suffix = fileNameAttr[fileNameAttr.length - 1];
        return uploadFile(multipartFile, suffix);
    }

    /**
     * 通过URL获取文件（https）
     * @param fileUrl
     * @return
     */
    public static File getNetUrlHttps(String fileUrl) {
        //对本地文件进行命名
        String file_name = reloadFile(fileUrl);
        File file = null;

        DataInputStream in = null;
        DataOutputStream out = null;
        try {
            file = File.createTempFile("net_url", file_name);

            SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
            sslcontext.init(null, new TrustManager[]{new X509TrustUtiil()}, new java.security.SecureRandom());
            URL url = new URL(fileUrl);
            HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslsession) {
//                    logger.warn("WARNING: Hostname is not matched for cert.");
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
            HttpsURLConnection urlCon = (HttpsURLConnection) url.openConnection();
            urlCon.setConnectTimeout(6000);
            urlCon.setReadTimeout(6000);
            int code = urlCon.getResponseCode();
            if (code != HttpURLConnection.HTTP_OK) {
                throw new Exception("文件读取失败");
            }
            // 读文件流
            in = new DataInputStream(urlCon.getInputStream());
            out = new DataOutputStream(new FileOutputStream(file));
            byte[] buffer = new byte[2048];
            int count = 0;
            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
            out.close();
            in.close();
        } catch (Exception e) {
//            log.error("远程图片获取错误："+fileUrl);
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.close();
                }
                if (null != in) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }

        return file;
    }

    /**
     * 通过URL获取文件（http）
     * @param fileUrl
     * @return
     */
    public static File getNetUrlHttp(String fileUrl) {
        //对本地文件命名
        String fileName = reloadFile(fileUrl);
        File file = null;


        URL urlfile;
        InputStream inStream = null;
        OutputStream os = null;
        try {
            file = File.createTempFile("net_url", fileName);
            //下载
            urlfile = new URL(fileUrl);
            inStream = urlfile.openStream();
            os = new FileOutputStream(file);

            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = inStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            log.error("远程图片获取错误："+fileUrl);
            e.printStackTrace();
        } finally {
            try {
                if (null != os) {
                    os.close();
                }
                if (null != inStream) {
                    inStream.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return file;
    }

    public static String reloadFile(String oleFileName) {
        oleFileName = getFileName(oleFileName);
        if (StringUtils.isEmpty(oleFileName)) {
            return oleFileName;
        }
        //得到后缀
        if (oleFileName.indexOf(".") == -1) {
            //对于没有后缀的文件，直接返回重命名
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
        String[] arr = oleFileName.split("\\.");
        // 根据uuid重命名图片
        String fileName =  UUID.randomUUID().toString().replaceAll("-", "") + "." + arr[arr.length - 1];

        return fileName;
    }

    public static String getFileName(final String url) {
        if (StringUtils.isEmpty(url)) {
            return url;
        }
        String newUrl = url;
        newUrl = newUrl.split("[?]")[0];
        String[] bb = newUrl.split("/");
        String fileName = bb[bb.length - 1];
        return fileName;
    }

    public static final Map<String, String> EXT_MAPS = new HashMap<>();

    /**
     * 文件下载
     * @param path
     * @param response
     */
    @Override
    public void fileDownLoad(String path, HttpServletResponse response) {
        //1.判断文件名称和路径是否存在，若不存在，就抛出异常
        //TODO 略

        InputStream is = null;
        OutputStream os = null;
        String fileName = path.substring(path.lastIndexOf("/") + 1);
        try {
            String contentType = EXT_MAPS.get(getFilenameSuffix(fileName));
            StorePath storePath = StorePath.parseFromUrl(path);
            byte[] content = downloadFile(storePath);
            if (content == null) {
                throw new ServerException("文件为空");
            }
            if (response != null) {
                os = response.getOutputStream();
                // 设置响应头
                if (StringUtils.isNotBlank(contentType)) {
                    // 文件编码 处理文件名中的 '+'、' ' 特殊字符
                    String encoderName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20").replace("%2B", "+");
                    response.setHeader("Content-Disposition", "attachment;filename=" + encoderName);
                    response.setContentType(contentType + ";charset=UTF-8");
                    response.setHeader("Accept-Ranges", "bytes");
                }
            }
            is = new ByteArrayInputStream(content);
            byte[] buffer = new byte[1024 * 5];
            int len = 0;
            while ((len = is.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
            os.flush();
        } catch (Exception e) {
//            LOGGER.error("文件下载异常：{}", e.getMessage(), e);
            //这里可以换成自定义异常
            throw new RuntimeException();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * fastDFS文件下载
     *
     * @param storePath
     * @return ResponseEntity<byte[]>
     */
    private byte[] downloadFile(StorePath storePath) {
        return fileStorageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadCallback<byte[]>() {
            @Override
            public byte[] recv(InputStream inputStream) throws IOException {
                byte[] buffer = null;
                ByteArrayOutputStream bos = new ByteArrayOutputStream(512);
                byte[] b = new byte[512];
                int n;
                while ((n = inputStream.read(b)) != -1) {
                    bos.write(b, 0, n);
                }
                inputStream.close();
                bos.close();
                buffer = bos.toByteArray();
                return buffer;
            }
        });
    }

    public static String getFilenameSuffix(String filename) {
        String suffix = null;
        String originalFilename = filename;
        if (StringUtils.isNotBlank(filename)) {
            if (filename.contains("/")) {
                filename = filename.substring(filename.lastIndexOf("/") + 1);
            }
            if (filename.contains(".")) {
                suffix = filename.substring(filename.lastIndexOf(".") + 1);
            }
//            } else {
//                if (LOGGER.isErrorEnabled()) {
//                    LOGGER.error("filename error without suffix : {}", originalFilename);
//                }
//            }
        }
        return suffix;
    }

    @PostConstruct
    private void initExt() {
        // image
        EXT_MAPS.put("png", "image/png");
        EXT_MAPS.put("gif", "image/gif");
        EXT_MAPS.put("bmp", "image/bmp");
        EXT_MAPS.put("ico", "image/x-ico");
        EXT_MAPS.put("jpeg", "image/jpeg");
        EXT_MAPS.put("jpg", "image/jpeg");
        // 压缩文件
        EXT_MAPS.put("zip", "application/zip");
        EXT_MAPS.put("rar", "application/x-rar");
        // doc
        EXT_MAPS.put("pdf", "application/pdf");
        EXT_MAPS.put("ppt", "application/vnd.ms-powerpoint");
        EXT_MAPS.put("xls", "application/vnd.ms-excel");
        EXT_MAPS.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        EXT_MAPS.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
        EXT_MAPS.put("doc", "application/msword");
        EXT_MAPS.put("doc", "application/wps-office.doc");
        EXT_MAPS.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        EXT_MAPS.put("txt", "text/plain");
        // 音频
        EXT_MAPS.put("mp4", "video/mp4");
        EXT_MAPS.put("flv", "video/x-flv");
    }

}
